package je3.nio;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;

public class FileCopy3 {
    public static void main(String[] args) throws IOException {
        // Open file streams and get channels for them.
        ReadableByteChannel in = new FileInputStream(args[0]).getChannel();
        WritableByteChannel out;
        if (args.length > 1) out = new FileOutputStream(args[1]).getChannel();
        else out = Channels.newChannel(System.out);

        // Do the copy
        copy(in, out);

        // Exception-handling and stream-closing code has been omitted.
    }

    // Read all available bytes from one channel and copy them to the other.
    public static void copy(ReadableByteChannel in, WritableByteChannel out)
            throws IOException {
        // First, we need a buffer to hold blocks of copied bytes.
        ByteBuffer buffer = ByteBuffer.allocateDirect(32 * 1024);

        // Now loop until no more bytes to read and the buffer is empty
        while (in.read(buffer) != -1 || buffer.position() > 0) {
            // The read( ) call leaves the buffer in "fill mode".  To prepare
            // to write bytes from the buffer, we have to put it in "drain mode"
            // by flipping it: setting limit to position and position to zero
            buffer.flip();

            // Now write some or all of the bytes out to the output channel
            out.write(buffer);

            // Compact the buffer by discarding bytes that were written
            // and shifting any remaining bytes.  This method also
            // prepares the buffer for the next call to read( ) by setting the
            // position to the limit and the limit to the buffer capacity.
            buffer.compact();
        }
    }

    public static void copy1(ReadableByteChannel in, WritableByteChannel out)
            throws IOException {
        ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);

        while (in.read(buffer) != -1) {
            // Prepare the buffer to be drained
            buffer.flip();

            // Write to the channel; may block
            out.write(buffer);

            // If partial transfer, shift remainder down
            // If buffer is empty, same as doing clear( )
            buffer.compact();
        }

        // EOF will leave buffer in fill state
        buffer.flip();

        // Make sure that the buffer is fully drained
        while (buffer.hasRemaining()) {
            out.write(buffer);
        }
    }

    public static void copy2(ReadableByteChannel in, WritableByteChannel out)
            throws IOException {
        ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);

        while (in.read(buffer) != -1) {
            // Prepare the buffer to be drained
            buffer.flip();

            // Make sure that the buffer was fully drained
            while (buffer.hasRemaining()) {
                out.write(buffer);
            }

            // Make the buffer empty, ready for filling
            buffer.clear();
        }
    }

}
